using System;
using System.Collections.Generic;
using System.Linq;
using GemTowerDefence.GUI;
using GemTowerDefence.Towers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace GemTowerDefence
{
  /// <summary>
  /// This is the main type for your game
  /// </summary>
  public class Game : Microsoft.Xna.Framework.Game
  {
    readonly GraphicsDeviceManager _graphics;
    RelativeSpriteBatch _spriteBatch;
    readonly TileMap _map;
    private WaveManager _waveManager;
    private Player _player;
    private Toolbar _toolBar;
    private readonly List<Button> _buttons;
    private bool _paused;
    private KeyboardState _prevKeyState;
    private Pathfinder _pathFinder;

    public const int TextureSize = 24;
    public const int PanelSize = 10;

    public static Texture2D SelectionTexture { get; private set; }

    public WaveManager WaveManager
    {
      get { return _waveManager; }
    }

    public Player Player
    {
      get { return _player; }
    }

    public Game()
    {
      _buttons = new List<Button>();
      _graphics = new GraphicsDeviceManager(this);

      Window.AllowUserResizing = true;
      Window.Title = "Gem TD";
      // If the window size changes, then our  drawable area changes and the 
      // game graphics might not fit.  
      // Hook into DeviceReset event so we can resize the graphics.
      _graphics.DeviceReset += OnGraphicsComponentDeviceReset;

      Content.RootDirectory = "Content";
      IsMouseVisible = true;
      _map = new TileMap();
      
      _graphics.PreferredBackBufferWidth = _map.Width * TextureSize + TextureSize * 10;
      _graphics.PreferredBackBufferHeight = _map.Height * TextureSize;
      _graphics.ApplyChanges();
    }

    private void OnGraphicsComponentDeviceReset(object sender, EventArgs e)
    {
      _spriteBatch.Resize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
    {
      // Create a new SpriteBatch, which can be used to draw textures.
      _spriteBatch = new RelativeSpriteBatch(GraphicsDevice);

      SelectionTexture = Content.Load<Texture2D>(@"Textures\selection");

      var topBar = Content.Load<Texture2D>(@"Textures\tool bar");
      var font = Content.Load<SpriteFont>("Segoe UI");
      _toolBar = new Toolbar(_map, topBar, font);

      //      Tile.TileSetTexture = Content.Load<Texture2D>(@"Textures\TileSets\map_tileset_24");
      _map.AddTexture(TileType.Grass, Content.Load<Texture2D>(@"Textures\grass"));
      _map.AddTexture(TileType.Road, Content.Load<Texture2D>(@"Textures\road"));
      _map.AddTexture(TileType.Plate, Content.Load<Texture2D>(@"Textures\plate"));

      var missileTexture = Content.Load<Texture2D>(@"Textures\missile");
      var towerTextures = new Dictionary<TowerType, Texture2D>
            {
              {TowerType.Rock, Content.Load<Texture2D>(@"Textures\Towers\rock")},
              {TowerType.Amethyst, Content.Load<Texture2D>(@"Textures\Towers\amethyst")},
              {TowerType.Aquamarine, Content.Load<Texture2D>(@"Textures\Towers\aquamarine")},
              {TowerType.Emerald, Content.Load<Texture2D>(@"Textures\Towers\emerald")}
            };

      var count = 0;
      foreach (var type in towerTextures.Keys)
      {
        var name = type.ToString().ToLower();
        var offset = count++ * TextureSize;
        var btn = new Button(type,
          Content.Load<Texture2D>(string.Format(@"Textures\Towers\{0}-button", name)),
          Content.Load<Texture2D>(string.Format(@"Textures\Towers\{0}-hover", name)),
          Content.Load<Texture2D>(string.Format(@"Textures\Towers\{0}-pressed", name)),
          new Vector2(_map.Width * TextureSize + 5 + offset, 5));
        btn.OnPress += CreateTowerButtonPress;
        _buttons.Add(btn);
      }

      _player = new Player(_map, towerTextures, missileTexture);

      var enemyTexture = Content.Load<Texture2D>(@"Textures\enemy");
      var healthTexture = Content.Load<Texture2D>(@"Textures\health bar");
      _waveManager = new WaveManager(_player, _map, 24, enemyTexture, healthTexture);

    }

    protected override void Initialize()
    {
      base.Initialize();
      _pathFinder = new Pathfinder(this);
      _pathFinder.LoadContent();
    }

    private void CreateTowerButtonPress(object sender, TowerButtonClickEventArgs e)
    {
      if (_waveManager.AvailableGems > 0)
        _player.NewTowerType = e.Type;
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
      var keyState = Keyboard.GetState();
      var mouseState = Mouse.GetState();

#if DEBUG
      Window.Title = "Gem TD " + mouseState;
#endif

      _pathFinder.Update(gameTime);

      // Allows the default game to exit on Xbox 360 and Windows
      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || AltComboPressed(keyState, Keys.F4) || keyState.IsKeyDown(Keys.Escape))
        Exit();

      if (_paused == false)
      {
        foreach (var button in _buttons)
          button.Update(gameTime);

        _waveManager.Update(gameTime);
        _player.Update(gameTime, _waveManager.Enemies);
      }
      if (keyState.IsKeyUp(Keys.Space) && _prevKeyState.IsKeyDown(Keys.Space))
      {
        // Toggle paused from either true to false
        // or false to true
        _paused = !_paused;
      }
      _prevKeyState = keyState;

      base.Update(gameTime);
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.CornflowerBlue);

      _spriteBatch.Begin();

      _map.Draw(_spriteBatch);
      _pathFinder.Draw(_spriteBatch);
      _player.Draw(_spriteBatch);
      _waveManager.Draw(_spriteBatch);

      _toolBar.Draw(_spriteBatch, _player, _waveManager);
      
      foreach (var button in _buttons)
        button.Draw(_spriteBatch);

      _player.DrawPreview(_spriteBatch);

      _spriteBatch.End();

      base.Draw(gameTime);
    }

    /// <summary>
    /// Checks whether an alt+key combo is pressed.
    /// </summary>
    private static bool AltComboPressed(KeyboardState state, Keys key)
    {
      return state.IsKeyDown(key) &&
             (state.IsKeyDown(Keys.LeftAlt) ||
              state.IsKeyDown(Keys.RightAlt));
    }

  }
}
